package handler

import (
	"errors"
	"fmt"
	"log"
	"moft/database"
	"moft/model"
	"net/http"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

type RoleHandler struct {
	db *gorm.DB
}

func NewRoleHandler() *RoleHandler {
	return &RoleHandler{db: database.DB()}
}

func (h *RoleHandler) Create(c *gin.Context) {
	var payload struct {
		RoleName string `json:"role_name" binding:"required"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
		return
	}

	// check admin.
	if model.IsAdminByRole(payload.RoleName) {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "the role is exist, please retype",
		})
		return
	}

	// check duplication.
	_, exist, err := h.checkDuplicateRoles(payload.RoleName)
	if err != nil {
		log.Printf("find role by name %s failed: %v", payload.RoleName, err)
		c.Status(http.StatusInternalServerError)
		return
	}
	if exist {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "the role is exist, please retype",
		})
		return
	}

	// create role.
	r, err := model.CreateRole(h.db, &model.Role{
		RoleName: payload.RoleName,
		Enabled:  true,
	})
	if err != nil {
		log.Printf("create role(%s) failed: %v", payload.RoleName, err)
		c.Status(http.StatusInternalServerError)
		return
	}

	c.JSON(http.StatusCreated, gin.H{
		"role": r,
	})
}

func (h *RoleHandler) List(c *gin.Context) {
	roles, err := model.ListRoles(h.db, nil)
	if err != nil {
		c.Status(http.StatusInternalServerError)
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"roles": roles,
	})
}

func (h *RoleHandler) Update(c *gin.Context) {
	var payload struct {
		RoleName string `json:"role_name"  binding:"required"`
		Enabled  bool   `json:"enable" binding:"required"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "the requested body error",
			"message": err.Error(),
		})
		return
	}

	roleName := c.Param("role_name")
	r, exist, err := h.checkDuplicateRoles(roleName)
	if err != nil {
		log.Printf("check duplicate roles failed: %v", err)
		return
	}
	if !exist {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "the role name does not exist",
		})
		return
	}
	_, exist, err = h.checkDuplicateRoles(payload.RoleName)
	if err != nil {
		log.Printf("check duplicate roles failed: %v", err)
		return
	}
	if exist {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": fmt.Sprintf("the role name %s is exist, please retype", payload.RoleName),
		})
		return
	}

	var update bool
	if r.RoleName != payload.RoleName {
		r.RoleName = payload.RoleName
		update = true
	}
	if r.Enabled != payload.Enabled {
		r.Enabled = payload.Enabled
		update = true
	}
	if update {
		if err := model.UpdateRole(h.db, r); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"error":   "update role failed",
				"message": err.Error(),
			})
			return
		}
	}

	c.Status(http.StatusCreated)
}

func (h *RoleHandler) checkDuplicateRoles(roleName string) (*model.Role, bool, error) {
	r, err := model.FindRoleByName(h.db, roleName)
	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, false, nil
	} else if err != nil {
		return nil, false, err
	}

	return r, true, nil
}
